#include "../include/mytools.h"

namespace zzyserver
{
	class FClient
	{
		
		private:
			MyTools tools;
		public:
		void parse_xml(char *back_str, const char *xml_str, const char *pre_str, const char *suf_str)
		{
			if (DEBUG_LOG)
				printf("L(%d) xml_str:%s pre_str:%s suf_str:%s \n", __LINE__, xml_str, pre_str, suf_str);
			int offset = strlen(pre_str);
			auto ptr_first = strstr(xml_str, pre_str);
			auto ptr_end = strstr(xml_str, suf_str);

			if ((ptr_first != NULL) && (ptr_end != NULL))
			{
				ptr_first = ptr_first + offset;
				strncpy(back_str, ptr_first, (ptr_end - ptr_first));
				if (DEBUG_LOG)
					printf("L(%d) xml data back_str:%s\n", __LINE__, back_str);
			}
		}

		bool recvfile(int sockfd, std::string path, std::string sourcefile_name, std::string file_name, char *buffer, long file_size)
		{
			printf("start downloading %s...\n", sourcefile_name.c_str());
			int file_fp;
			long recv_sum = 0;		  //统计接收文件总大小
			int progress = 50;		  //进度条的长度
			int current = 0;		  //当前进度
			// 打开文件，准备写入
			std::string newfile_name = path;
			if(newfile_name.size() == 1 && newfile_name[0] != '\0')
			{
				if(newfile_name[0] == '.')
					newfile_name[0] = '\0';
				else if(newfile_name[0] != '/')
					newfile_name += '/';
			}
			else newfile_name += '/';
			newfile_name += file_name;
			//printf("create file: %s\n", newfile_name);
			file_fp = open(newfile_name.c_str(), O_CREAT | O_RDWR, 0777);
			if (file_fp < 0)
			{
				printf("file: %s can not open to write.\n", newfile_name.c_str());
				return false;
			}
			int length = 0;
			int hasrecv = 0;
			while(file_size > 0 && (length = recv(sockfd, buffer, BUFFER_SIZE, 0)) > 0) 
			{ 
				if( write(file_fp, buffer, length ) < length) 
				{ 
					printf("file: %s write failed\n", newfile_name.c_str()); 
					close(file_fp);
					return false; 
				} 
				recv_sum += length; //累计接收文件大小
				hasrecv += length;
				//计算当前进度
				current = recv_sum * progress / file_size;
				//打印进度条
				
				printf("\r");
				printf("[");
				for (int i = 0; i < progress; i++)
				{
					if (i < current)
						printf("="); //已接收部分
					else
						printf("+"); //未接收部分
				}
				printf("]");
				printf(" %8ld/%ld %6.2f%%", recv_sum, file_size, (float)recv_sum / file_size * 100);
				if(hasrecv > MAX_SENDLENGTH)
				{
					if (send(sockfd, "#beginsend#", 11, 0) < 0)
						printf("\nfile: %s send #beginsend# failed.\n", newfile_name.c_str());
					hasrecv = 0;
				}
				memset( buffer,0, BUFFER_SIZE );
				if(recv_sum >= file_size)break;
			} 
			if (send(sockfd, "#successful#", 12, 0) < 0)
				printf("\nfile: %s send #successful# failed.\n", newfile_name.c_str());
			// 接收成功后，关闭文件，关闭socket 
			printf("\nOK %s.\n", newfile_name.c_str()); 
			close(file_fp); 
			return true;	
		}

		bool recvdir(int sockfd, std::string path, std::string sourcefile_name, std::string file_name, char * buffer)
		{
			//创建文件夹
			printf("start downloading %s...\n", sourcefile_name.c_str());
			std::string newfile_path = path;
			if(newfile_path[0] != '\0' && newfile_path.size() == 1)
			{
				if(newfile_path[0] == '.')
					newfile_path = "";
				else if(newfile_path[0] != '/')
					newfile_path += "/";
			}
			else newfile_path += "/";
			newfile_path += file_name;
			int mkflag = mkdir(newfile_path.c_str(), 0777);
			if(mkflag < 0)
			{
				printf("mkdir: %s error.", newfile_path.c_str());
				return false;
			}
			//接收头部消息
			bool recvflag = true;
			while(1)
			{
				int length = 0; 
				memset( buffer, 0, BUFFER_SIZE);
				length = recv(sockfd, buffer, BUFFER_SIZE, 0);
				if (length < 0)
				{
					perror("server recieve data failed: ");
					return false;
				}
				//printf("%s\n", buffer);
				if(!strncmp(buffer, "#enddir#", 8)){ break;}
				if(send(sockfd, "#over#", 6, 0) < 0)
				{
					printf("#over# recv error.\n");
					exit(1);
				}
				TCP_STRUCT_DATA struct_data;
				memset(&struct_data, 0x0, sizeof(struct_data));
				memcpy(&struct_data, buffer, sizeof(struct_data)); 
				long file_size = 0;
				std::string newfile_name;
				if (struct_data.m_data_len > 0) // m_data_len>0 则仍有数据要接收
				{
					length = recv(sockfd, buffer, struct_data.m_data_len, 0);
					if (length < 0)
					{
						printf("server recieve data failed.");
						return false;
					}
					
					//解析xml
					char file_size_tmp[8] = {0};
					char file_name2[FILENAME_MAX] = {0};
					parse_xml(file_name2, buffer, "<name>", "</name>");
					parse_xml(file_size_tmp, buffer, "<size>", "</size>");
					newfile_name = file_name2;
					if (newfile_name.size() > 0 && newfile_name[0] != '\0')
						file_size = atol(file_size_tmp);
					else{
						printf("parse xml error！\n");
						return false;
					}
					if(send(sockfd, "#over#", 6, 0) < 0)
					{
						printf("#over# recv error.\n");
						exit(1);
					}
				}
				
				if(struct_data.m_cmd == GET_DIR)
				{
					recvflag = recvdir(sockfd, newfile_path, newfile_name, newfile_name, buffer);
				}
				else if(struct_data.m_cmd == GET_FILE)
				{
					recvflag = recvfile(sockfd, newfile_path, newfile_name, newfile_name, buffer, file_size);
				}
				if(!recvflag)
				{
					printf("failed to download %s.\n", newfile_path.c_str());
					return false;
				}
				
			}
			if (send(sockfd, "#dirsuccessful#", 15, 0) < 0)
				printf("file: %s send #successful# failed.\n", newfile_path.c_str());
			printf("OK %s.\n", newfile_path.c_str()); 
			return true;
		}

		int recvfromserver(int sockfd, Popt opt)
		{ 
			long file_size = 0;		  //文件总大小
			auto file_name = opt->sourcename;
			auto out_file_name = opt->outfilename;
		
			char buffer[BUFFER_SIZE]; 
			memset(buffer, 0, BUFFER_SIZE );
			strncpy(buffer, file_name.c_str(), file_name.size() > BUFFER_SIZE ? BUFFER_SIZE : file_name.size()); 
			
			// 向服务器发送buffer中的数据 
			if(send(sockfd, buffer, BUFFER_SIZE, MSG_NOSIGNAL) < 0) 
			{ 
				perror("send file name failed:"); 
				exit(1); 
			} 
			if(tools.is_exist_file(".", out_file_name) >= 0)
			{
				printf("failed to download %s: %s already exists.\n", out_file_name.c_str(), out_file_name.c_str());
				exit(1);
			}
			// 从服务器接收数据到buffer中 
			
			int length = 0; 
			memset( buffer, 0, BUFFER_SIZE);
			length = recv(sockfd, buffer, BUFFER_SIZE, 0);
			if(strncmp(buffer, "#file not found#", 16) == 0)
			{
				printf("failed to download %s: %s is not exists.\n", file_name.c_str(), file_name.c_str());
				exit(1);
			}
			struct TCP_STRUCT_DATA struct_data;
			memset(&struct_data, 0x0, sizeof(struct_data));
			memcpy(&struct_data, buffer, sizeof(struct_data)); //把结构体的数据通过memcpy的方式拷贝到struct_data中
			//printf("cmd:%d data_len:%d\n", struct_data.m_cmd, struct_data.m_data_len);
			memset( buffer, 0, BUFFER_SIZE);
			if(send(sockfd, "#over#", 6, 0) < 0)
			{
				printf("#over# send error.\n");
				exit(1);
			}
			if (struct_data.m_data_len > 0) // m_data_len>0 则仍有数据要接收
			{
				length = recv(sockfd, buffer, struct_data.m_data_len, 0);
				if (length < 0)
				{
					printf("server recieve data failed.");
					return -1;
				}
				//解析xml
				
				char file_size_tmp[8] = {0};
				char file_name2[FILENAME_MAX] = {0};
				parse_xml(file_name2, buffer, "<name>", "</name>");
				parse_xml(file_size_tmp, buffer, "<size>", "</size>");
				file_name = file_name2;
				if (file_name.size() > 0 && file_name[0] != '\0')
					file_size = atol(file_size_tmp);
				else
				{
					printf("parse xml error！\n");
					return -1;
				}
				
				if(send(sockfd, "#over#", 6, 0) < 0)
				{
					printf("#over# send error.\n");
					exit(1);
				}
			}
			bool recvflag = true;
			
			if(struct_data.m_cmd == GET_DIR)
			{
				recvflag = recvdir(sockfd, ".", file_name, out_file_name, buffer);
			}
			else if(struct_data.m_cmd == GET_FILE)
			{
				recvflag = recvfile(sockfd, ".", file_name, out_file_name, buffer, file_size);
			}
			if(!recvflag)
			{
				return -1;
			}
			return 0;
			
		}

		void outpathfile(int sockfd, Popt opt)
		{
			char buffer[BUFFER_SIZE] = {0};
			int length = 0; 
			if(send(sockfd, "#beginpath#", 11, 0) < 0)
			{
				printf("#beginpath# recv error.\n");
				exit(1);
			}
			memset( buffer, 0, BUFFER_SIZE);
			length = recv(sockfd, buffer, BUFFER_SIZE, 0);
			if (length < 0)
			{
				perror("server recieve data failed: ");
				exit(1);
			}
			
			if(send(sockfd, "#over#", 6, 0) < 0)
			{
				printf("#over# recv error.\n");
				exit(1);
			}
			struct TCP_STRUCT_DATA struct_data;
			memset(&struct_data, 0x0, sizeof(struct_data));
			memcpy(&struct_data, buffer, sizeof(struct_data)); 
			long totolsize = struct_data.m_data_len;
			//printf("file length: %ld\n", totolsize);
			printf("file lists:\n");
			
			int sumlength = 0; 
			int hasrecv = 0;
			while((length = recv(sockfd, buffer, BUFFER_SIZE-1, 0)) > 0)
			{
				sumlength += length;
				hasrecv += length;
				if(opt->show)
					printf("%s", buffer);
				if(hasrecv > MAX_SENDLENGTH)
				{
					if (send(sockfd, "#beginsend#", 11, 0) < 0)
						printf("file: %s send #beginsend# failed.\n", opt->sourcename.c_str());
					hasrecv = 0;
				}
				memset( buffer, 0, BUFFER_SIZE);
				if(sumlength >= totolsize)break;
			}
			if(send(sockfd, "#pathover#", 10, 0) < 0)
			{
				printf("#pathover# recv error.\n");
				exit(1);
			}
		}
	
		void start(int argc, char *argv[])
		{
			//解析参数
			Opt opt = Opt();
			memset(&opt , 0, sizeof(Opt));
			opt.show = true;
			opt.ip = "localhost";
			opt.port = SERVER_PORT;
			struct option long_options[] = 
			{
				{"list", no_argument, 0, 'l'},
				{"server", required_argument, 0, 's'},
				{"port", required_argument, 0, 'p'},
				{"dl", required_argument, 0, 'd'},
				{"output", required_argument, 0, 'o'},
				{"help", no_argument, 0, 'h'},
				{0, 0, 0, 0}};
			std::string shortopts = "ls:p:d:o:h";
			if(!tools.parseopt(argc, argv, &opt, long_options, shortopts, false))
				return ;
			

			//获取服务器信息
			struct hostent *h;
			h = gethostbyname(opt.ip.c_str());
			if(nullptr == h)
			{
				perror("geyhostbyname");
				exit(1);
			}
			//初始化套接字
			int sockfd=socket(AF_INET, SOCK_STREAM, 0);
			if(-1 == sockfd)
			{
				perror("socket");
				exit(1);
			}
			struct sockaddr_in server_addr;
			server_addr.sin_family=AF_INET;
			server_addr.sin_port=htons(opt.port);
			server_addr.sin_addr=*((struct in_addr *)h->h_addr_list[0]);
			if(-1==connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)))
			{
				printf("failed to connect to %s:%d\n", opt.ip.c_str(), opt.port);
				exit(1);
			}
			
			if(opt.sourcename.size() == 0 || opt.sourcename[0] == '\0')
			{
				outpathfile(sockfd, &opt);
				return ;
			}
			if(opt.outfilename.size() == 0 || opt.outfilename[0] == '\0')
				opt.outfilename = opt.sourcename;
			if(recvfromserver(sockfd, &opt) == -1)
				exit(1);
		}
	};
}

int main(int argc, char *argv[])
{
	zzyserver::FClient fclient;
	fclient.start(argc, argv);
	return 0;
}



